
const gjjTokenUrl   = "https://t.51gjj.com/gjj/getToken";
const getOrderSnUrl = "https://t.51gjj.com/gjj/getOrderSn";
const getGjjH5Url   = "https://t.51gjj.com/gjj";
const gjjDataUrl    = "http://b.jianbing.com/gjj_v3/gjj/fetch_manual";
const getGjjDataUrl = "https://t.51gjj.com/gjj/getGjjData";

const NAME_AES_KEY = 'zt2i6cea0fceabb063e3961gg67op345';//name、id
const PASS_AES_KEY = 'f1e24cea0fceaaa063e3961ff187f1e6';//password

var myRedis = require('./myRedis').config('6379', '127.0.0.1');
var myAxios =  require('./myAxios');

var fs          =  require('fs');
var config      =  require('config');
var parCfg      =  config.get('partnerConfig');
var NodeRSA     =  require('node-rsa');
var express     =  require('express');
var querystring =  require('querystring');
// var nodeDB      =  require('./db');
var md5         =  require('md5');
var url         =  require('url');
var request     =  require('request');
var myCrypto      =  require('crypto');

const tokenName = "partner_token_"+parCfg.cid;
const lockName = "token_lock_"+parCfg.cid;

/**
 * 获取token
 * @param expiresTime 保存时间 单位秒
 * @param refresh  强制更新
 * @returns {Promise.<*>} token
 */
async function getTokenToRedis(expiresTime,refresh) {
    expiresTime = expiresTime || 7200;
    refresh = refresh || false;
    let token = null;

    let insRedis = myRedis.create();
    let reloading = function (){
        let i = 0;
        return new Promise(resolve=>{
            let myTimer = setInterval(
                async function(){
                    token = await insRedis.get(tokenName);
                    //console.error(token)
                    console.error('loading modify '+tokenName+':'+token);
                    if(token!==null || i>30){
                        clearTimeout(myTimer);
                        resolve(token);
                    }
                },
                1000
            )}
        );
    }

    token = await insRedis.get(tokenName);
    //console.log('old token is:' + token);

    if (token === null || refresh) {
        let ifSet = await insRedis.set(lockName, 'lock', 'EX', 30, 'NX');
        console.error('ifLock ' + (ifSet?'锁成功...':'等待token...'));
        if (ifSet) {
            // //获取token
            let data = [];
            data['timestamp'] = +new Date;
            data['appKey'] = parCfg.reptileAppKey;
            let appSecret = parCfg.reptileAppSecret;
            let sign = createSign(data,appSecret);
            //token参数
            let params = {
                "sign":sign,
                "params":{
                    "appKey":data['appKey'],
                    "timestamp":data['timestamp']
                }
            }

            let result = await myAxios.post(gjjTokenUrl,params,{headers:{'Content-Type':'application/json'}});
            if(result){
                if(result.data.code==0){
                    //写入token
                    token = result.data.data.token;
                    await insRedis.set(tokenName, token, 'EX', expiresTime);
                    //复读
                    // token = await insRedis.get(tokenName);
                }
            }
            if(typeof token!='string'){
                insRedis.del(lockName);
                token = await reloading();
            }

        } else {
            //遇到锁|等待
            token = await reloading();
        }
    }
    //console.log('new token is:' + token);
    return token;
}

/**
 * 获取order
 * @returns {Promise.<*>}order
 */
async function getOrder(){
    let data = [];
    data['timestamp'] = +new Date;
    data['appKey']    = parCfg.reptileAppKey;
    data['token']     = await getTokenToRedis();
    let appSecret     = parCfg.reptileAppSecret;
    let sign = createSign(data,appSecret);

    let params = {
        "sign":sign,
        "params":{
            "appKey":data['appKey'],
            "timestamp":data['timestamp'],
            "token":data['token']
        }
    }

    let orderSn = null;
    let result = await myAxios.post(getOrderSnUrl,params,{headers:{'Content-Type':'application/json'}});
    if(result){
        if(result.data.code==0){
            //写入token
            orderSn = result.data.data.orderSn;
            return orderSn;
        }else if(result.data.msg=='无效的token'){
            console.error('【wld】【getOrder】无效的token，已经重试，稍后再请求一次');
            await getTokenToRedis(7200, true);
        }
    }

    throw "get orderSn fail";
}

/**
 * 服务端token
 * @param size 长度
 */
function createOldToken(size){
    if(!size){size=32;}
    var token = randomString(size);
    return token;
}

/**
 * 服务端openid
 * @param num 长度
 * @returns {string}
 */
function createOldOpenid(num){
    if(!num){num=0;}
    var r = new Array('Q', 'w', 'E', '8', 'a', 'S', '2', 'd', 'Z', 'x', '9', 'c', '7', 'p', 'O', '5', 'i', 'K', '3', 'm', 'j', 'U', 'f', 'r', '4', 'V', 'y', 'L', 't', 'N', '6', 'b', 'g', 'H');
    var b = new Array('q', 'W', 'e', 'A', 's', 'D', 'z', 'X', 'C', 'P', 'o', 'I', 'k', 'M', 'J', 'u', 'F', 'R', 'v', 'Y', 'T', 'n', 'B', 'G', 'h');
    var l = r.length;
    var s = 12;
    var buf = new Array();
    var charPos = 32;
    for(var i=0;i<charPos;i++){
        buf[i]=' ';
    }
    while (Math.round(num / l) > 0) {
        buf[--charPos] = r[Math.round(num % l)];
        num /= l;
    }
    buf[--charPos] = r[Math.round(num % l)];
    buf = buf.join('');
    var str = buf.substr(charPos, (32 - charPos));
    //不够长度的自动随机补全
    if ( str.length < s) {
        var sb = '';
        for (i = 0; i < s - str.length; i++) {
            sb += b[Math.round(Math.random()*(b.length-1))];
        }
        str += sb;
    }
    return str;
}

/**
 * JSON POST请求
 * @param serUrl
 * @param params
 * @returns {Promise.<result.data|{crawlTaskType, crawlTaskSubType, type, description, finished, taskId, isCanLeave, waitSeconds, resultDetail, progress, totalProgress, phase, timestamp}>}
 */
async function jsonPost(serUrl, params){
    let result = await myAxios.post(serUrl,params,{headers: {'Content-Type': 'application/json'}});
    return result.data;
}

/**
 * GET请求
 * @param serUrl
 * @param params
 * @returns {Promise.<*>}
 */
async function getQuest(serUrl, params){
    return await myAxios.get(serUrl,params)
}

/**
 * 随机字符转
 * @param size
 * @returns {string}
 */
function randomString (size){
    if(!size){size=32;}
    var a = "-_0987654321asdfghjklzxcvbnmqwertyuiopASDFGHJKLZXCVBNMQWERTYUIOP";
    var len = a.length;
    var result = '';
    for(var i=0; i<size; i++) {
        result += a[0+Math.round(Math.random()*len)];
    }
    return result;
}

//序列化
function urlBase64Encode(str) {
    let buf = new Buffer(str);
    let base64str = buf.toString('base64');
    base64str = base64str.replace(/\+/g,'-');
    base64str = base64str.replace(/\//g,'_');
    base64str = base64str.replace(/=/g,'');
    console.log(base64str);
    return base64str;
}

function urlBase64Decode(base64str) {
    base64str = base64str.replace(/-/g,'+');
    base64str = base64str.replace(/_/g,'/');
    let mod4 = base64str.length % 4;
    if(mod4){
        base64str += '===='.substr(mod4);
    }
    let buf = new Buffer(base64str,'base64');
    let str = buf.toString('utf8');
    return str;
}


//脱敏加解密集合
function decodeName(str){}
function encodeName(str){}
function decodePass(str,userSid){}
function encodePass(str,userSid){}

/**
 * rsa加密
 * @param str 待加密明文
 * @param publicKeyFileName 密钥路径或文件名
 * @returns {string}
 */
function myRsaEncrypt(str, publicKeyFileName){
    let rsaEncodeData = "";
    try {

        let rsaPublicFile  = __projectDir+'/ca/'+publicKeyFileName;
        let rsaPublicKeyStr = fs.readFileSync(rsaPublicFile).toString('ascii');

        let rsaResource = new NodeRSA(rsaPublicKeyStr);
        rsaEncodeData = rsaResource.encrypt(str,'base64',rsaResource);
        rsaEncodeData = urlBase64Encode(rsaEncodeData);
    }catch(err) {
        console.log('rsa加密: '+err);
    }
    return rsaEncodeData;
}

/**
 * rsa解密
 * @param rsaEncodeData 待解密密文
 * @param rsaPrivateKeyStr 密钥路径或文件名
 * @returns {string}
 */
function myRsaDecrypt(rsaEncodeData, privateKeyFileName){
    var rsaDecodeData = "";

    try {
        rsaEncodeData = urlBase64Decode(rsaEncodeData);

        let rsaPrivateFile  = __projectDir+'/ca/'+privateKeyFileName;
        let rsaPrivateKeyStr = fs.readFileSync(rsaPrivateFile).toString('ascii');
        let rsaResource = new NodeRSA(rsaPrivateKeyStr);
        rsaDecodeData = rsaResource.decrypt(rsaEncodeData,'utf8');
    }catch(err){
        console.log('rsa解密: '+err);
    }
    return rsaDecodeData;
}

function _myRsaEncrypt(str, rsaPublicKeyStr){
    str = urlBase64Encode(str);
    var rsaEncodeData = "";
    try {
        var publickey = new NodeRSA(rsaPublicKeyStr);
        rsaEncodeData = publickey.encrypt(str,'base64');
    }catch(err) {
        console.log('rsa加密: '+err);
    }
    return rsaEncodeData;
}

function _myRsaDecrypt(rsaEncodeData, rsaPrivateKeyStr){
    //rsaPrivateKeyStr = rsaPrivateKeyStr.replace(/\n/g,'');
    //rsaPrivateKeyStr = rsaPrivateKeyStr.replace(/\r/g,'');
    let privateKey = {
        "key":rsaPrivateKeyStr,
        "passphrase":"",
        "padding":myCrypto.constants.RSA_NO_PADDING
    }
   // rsaEncodeData = base64Decode(rsaEncodeData);
    var rsaDecodeData = "";
    try {
        // let privatekey = new NodeRSA(rsaPrivateKeyStr);
        // rsaDecodeData = privatekey.decrypt(rsaEncodeData,'utf8');

        let buf = new Buffer(rsaEncodeData);
        let step = 64;
        let box = new Array();
        let x = 0;
        for(var i=0; i<buf.length; i+=step){
            box[x] = myCrypto.privateDecrypt(privateKey,new Buffer(rsaEncodeData.substr(i,step)));
            ++x;
        }
        rsaDecodeData = box.join('');

        //rsaDecodeData = myCrypto.privateDecrypt(privateKey,new Buffer(rsaEncodeData));
    }catch(err){
        console.log('rsa解密: '+err);
    }
    return rsaDecodeData;
}

/**
 * 获取请求参数的值
 * @param req
 * @param key
 * @param def
 * @returns {string}
 */
function getParams(req, key, def){
    var params = "";
    if(typeof req.body[key]!='undefined'){
        params = req.body[key];
    }else if(typeof url.parse(req.url, true).query[key]!='undefined'){
        params = url.parse(req.url, true).query[key];
    }
    if(!params){
        params = def;
    }
    return params;
}

/**
 * 获取数组或对象的值
 * @param data
 * @param key
 * @param def
 * @returns {*}
 */
function getValue(data, key, def){
    let res;
    if(typeof def!='undefined'){
        res = def;
    }
    if(typeof data!='undefined' && typeof data[key]!='undefined'){
        res = data[key];
    }

    return res;
}

/**
 * 创建签名
 * @param params
 * @param appSecret
 * @param version
 * @returns {*}
 */
function createSign(params,appSecret,version){
    var keys = new Array();
    var i = 0;

    for(x in params){
        if(params[x]){
            keys[i] = x;
            ++i;
        }
    }
    keys.sort();
    // console.log(keys);
    var sp = '';
    var stringA = '';
    var value;
    for (var x=0; x<keys.length; x++){
        if(keys[x]=='sign' || keys[x]=='signType'){
            continue;
        }
        value = params[keys[x]];

        if(value){
            stringA += sp + keys[x] + '=' + value;
            sp = '&';
        }
    }
    //差异appSecret  &  appsecret
    let appSecretKey;
    if(!version){
        //爬虫端
        appSecretKey = 'appSecret';
    }else{
        //原来服务端
        appSecretKey = 'appsecret';
    }
    stringA += sp + appSecretKey+'=' + appSecret;
    var md5StringA = md5(stringA);
    return md5StringA.toUpperCase();
}

/**
 * 合作方通知（间接通知）
 * @param url
 * @param params
 * @returns {Promise.<void>}
 */
async function notify(url,params){

    let content = "";
    for(x in params){
        if(!content){
            content += ('"'+x+'":"'+params[x]+'"');
        }else{
            content += (',"'+x+'":"'+params[x]+'"');
        }
    }
    content = '{'+content+'}';

    //是否加密通知
    //判断依据是否有合作方预留的公钥
    // if(parCfg.RSA_PARTNER_PUBLIC_KEY){
    //     let rsaPublicFileName  = __projectDir+'/ca/'+parCfg.RSA_PARTNER_PUBLIC_KEY;
    //     let rsaPublicKeyStr = fs.readFileSync(rsaPublicFileName).toString('ascii');
    //     content = myRsaEncrypt(content,rsaPublicKeyStr);
    // }

    let result = await myAxios.post(url,JSON.parse(content),{headers: {'Content-Type': 'application/json'}});
    if(result){
        let data = result.data;
        if(data.code==0){
            console.log('【wld】【notify】'+JSON.stringify(data.data));
        }else{
            console.error('【wld】【notify】fail msg 1 '+JSON.stringify(data.data));
        }
    }else{
        console.error('【wld】【notify】fail msg 2'+JSON.stringify(result));
    }
}

/**
 * 公积金关键标识参数
 * @param orderSn
 * @returns {Promise.<*>}
 */
async function getGjjSign(orderSn){
    let gjjData = await getGjjData(orderSn);
    if(gjjData){
        // console.log(data);
        if(gjjData.code==0){
            // let name   = "";
            let ID     = "";
            let name   = "";
            let cityId = "";
            let ifLoan = false;

            if(gjjData.data.gjjData.data.gjj_data){
                let gjjDataList = gjjData.data.gjjData.data.gjj_data;
                let loanDataList = gjjData.data.gjjData.data.loan_data;
                if(gjjDataList.length>0){
                    for(let i=0; i<gjjDataList.length; i++){
                        if(gjjDataList[i].gjj_account_analyzed_data.newest_account == 1){
                            ID     =  gjjDataList[i].gjj_brief.ID;
                            name   =  gjjDataList[i].gjj_brief.name;
                            cityId =  gjjData.data.gjjData.cid;
                        }
                    }
                }
                if(loanDataList.length>0){
                    ifLoan = true;
                }
            }

            let params = new Array();
            //参与md5的参数
            params['ID']     = ID;
            params['cityId'] = cityId;
            let gjjMD5 =  paramsSign(params);

            //有用到的参数
            params['name']   = name;
            params['ifLoan'] = ifLoan;

            return {gjjDataSign:gjjMD5,params:params};
        }else{
            console.error(gjjData.msg);
            return ''
        }
    }else{
        console.error('getGjjSign fail');
        return '';
    }
}

/**
 * 公积金数据拉取
 * @param orderSn
 * @returns {Promise.<*>}
 */
async function getGjjData(orderSn){
    let params = {
        appKey:parCfg.reptileAppKey,
        timestamp:+new Date,
        token:await getTokenToRedis(),
        orderSn:orderSn
    }
    let sign = createSign(params,parCfg.reptileAppSecret);
    let option = {
        "type":'gjj',
        "sign":sign,
        "params":params
    }
    return await jsonPost(getGjjDataUrl,option);
}

/**
 * 参数唯一话标识
 * @param params
 * @returns {*}
 */
function paramsSign(params){
    let keys = new Array();
    let i = 0;
    for(x in params){
        if(params[x]){
            keys[i] = x;
            ++i;
        }
    }
    keys.sort();
    let sp = '';
    let stringA = '';
    let value;
    for (var x=0; x<keys.length; x++){

        value = params[keys[x]];

        if(value){
            stringA += sp + keys[x] + '=' + value;
            sp = '&';
        }
    }
    let md5StringA = md5(stringA);
    let gjjMD5 =  md5StringA.toUpperCase();
    return gjjMD5
}

function getLocalInfo(term,name){
    let outData = {};
    var os = require('os');
    var IPv4,hostName;
    hostName=os.hostname();
    for(var i=0;i<os.networkInterfaces().en0.length;i++){
        if(os.networkInterfaces().en0[i].family=='IPv4'){
            IPv4=os.networkInterfaces().en0[i].address;
        }
    }
}

//接口丢出
exports.getToken = getTokenToRedis;
exports.getOrder = getOrder;
exports.getGjjData = getGjjData;
exports.getGjjSign = getGjjSign;
exports.createOldToken = createOldToken;
exports.createOldOpenid = createOldOpenid;

exports.rsaEncrypt = myRsaEncrypt;
exports.rsaDecrypt = myRsaDecrypt;
exports.base64Encode = urlBase64Encode;
exports.base64Decode = urlBase64Decode;

exports.getParams = getParams;
exports.getValue = getValue;
exports.createSign = createSign;

exports.jsonPost = jsonPost;
exports.getQuest = getQuest;

exports.notify = notify;
exports.getTokenToRedis = getTokenToRedis;
